home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
CD ROM Paradise Collection 4
/
CD ROM Paradise Collection 4 1995 Nov.iso
/
program
/
4cmp22s.zip
/
MULTI.4TH
< prev
next >
Wrap
Text File
|
1994-10-30
|
12KB
|
459 lines
\ ForthCMP Multitasking Module
\ Copyright 1987 (C) By Thomas Almy. All rights reserved.
\ Permission is granted to registered users of ForthCMP to sell or distribute
\ computer programs incorporating the compiled contents of this file.
\ IBM BIOS is used for terminal I/O.
\ See the manual for usage of this module.
\ IBM is a trademark of International Business Machines, Inc.
.( LOADING MULTI) CR
FIND EMIT? [IF] DROP 1 [ELSE] 0 [THEN] CONSTANT facl \ FACILITY Wordset used
INCLUDE INTS
INCLUDE FARMEM1
10
DECIMAL
0 0 IN/OUT NEED SINGLE
0 0 IN/OUT NEED MULTI
0 0 IN/OUT NEED PAUSE
0 0 IN/OUT NEED end-timer
0 0 IN/OUT NEED start-timer
VARIABLE ?multi \ true if multitasking turned on
VARIABLE user \ disp into user segment--used at compile time
VARIABLE CTASK \ pointer to task list
VARIABLE dispused \ semaphore for display output
VARIABLE inaccept \ executing ACCEPT -- only one at a time, please!
\ Semaphores
1 0 IN/OUT
: SEMA BEGIN DUP @ WHILE PAUSE REPEAT ON ;
1 0 IN/OUT
: PHORE OFF PAUSE ;
0 0 IN/OUT
: BYE end-timer bye ;
\ Memory management interface
1 1 IN/OUT
: GET malloc IF ." OUT OF MEMORY " BYE THEN ;
\ USER VARIABLES
H: UALLOT DSEG user @ + user ! ;
1 2 IN/OUT
H: UCREATE user @ CONSTANT ;
H: UVARIABLE UCREATE 2 UALLOT ;
H: URESET DSEG 0 user ! ;
URESET
\ redefinition of primitive I/O functions
HEX
0 0 IN/OUT
CODE setcursor \ set the cursor to the correct location
CTASK [] BX MOV
CS: 12 +[BX] DH MOV \ Y value
CS: 14 +[BX] DL MOV \ X value
BH BH XOR
2 # AH MOV
10 INT
RET
END-CODE \ setcursor
0 0 IN/OUT
CODE getcursor \ get the correct cursor coordinates
3 # AH MOV
BH BH XOR
10 INT
CTASK [] BX MOV
DH CS: 12 +[BX] MOV \ Y value
DL CS: 14 +[BX] MOV \ X value
RET
END-CODE \ getcursor
2 0 IN/OUT
: AT-XY CTASK @ 12 + CS: 2! ;
0 2 IN/OUT
: ?XY CTASK @ 12 + CS: 2@ ;
1 0 IN/OUT
CODE emit
0E # AH MOV
BX BX XOR
10 INT
RET
END-CODE
0 0 IN/OUT
CODE PAGE
CX CX XOR CX ES >SEG ES: 44A [] DL MOV DL DEC ES: 484 [] DH MOV
DH DX OR =0 IF, 18 # DH MOV THEN, 7 # BH MOV 600 # AX MOV
10 INT RET
END-CODE
0 1 IN/OUT
facl [IF]
CODE EKEY?
[ELSE]
CODE KEY?
[THEN]
CALL' PAUSE \ allow another task to execute
1 # AH MOV
16 INT
0 # AX MOV
=0 ~ IF, AX DEC THEN,
RET
END-CODE \ KEY?
: PAD CTASK @ 18 + CS: @ ;
DECIMAL
: EMIT
dispused SEMA
setcursor
emit
getcursor
dispused PHORE ;
: TYPE
dispused SEMA
setcursor
0 ?DO COUNT emit LOOP DROP
getcursor
dispused PHORE ;
: CS:TYPE
dispused SEMA
setcursor
0 ?DO CS: COUNT emit LOOP DROP
getcursor
dispused PHORE ;
: SPACES \ send out all characters in a burst
dispused SEMA
setcursor
DUP 0> IF 0 DO BL emit LOOP ELSE DROP THEN
getcursor
dispused PHORE ;
facl [IF]
VARIABLE pchr -1 pchr !
: KEY pchr @ 0< 0= IF pchr @ pchr ON EXIT THEN
BEGIN EKEY EKEY>CHAR 0= WHILE DROP REPEAT ;
: KEY? pchr @ 0< 0= IF TRUE EXIT THEN
BEGIN EKEY? setcursor WHILE EKEY EKEY>CHAR IF pchr ! TRUE EXIT THEN
DROP REPEAT FALSE ;
: EKEY BEGIN EKEY? setcursor UNTIL 0 7 BDOS
?DUP 0= IF BEGIN EKEY? setcursor UNTIL 0 7 BDOS 256 + THEN ;
[ELSE]
: KEY BEGIN KEY? setcursor UNTIL 0 8 BDOS ;
[THEN]
\ ACCEPT
0 0 IN/OUT
: bu 8 emit BL emit 8 emit ;
: ACCEPT
inaccept SEMA \ too hard if two or more tasks want input at once!
>R 0
BEGIN
KEY dispused SEMA setcursor CASE
[CTRL] [ OF 0 ?DO bu LOOP 0 ENDOF
[CTRL] H OF DUP IF bu 1- THEN ENDOF
[CTRL] M OF
NIP R> DROP
getcursor
dispused PHORE
inaccept PHORE
EXIT ENDOF
( ELSE ) OVER R@ <> IF DUP >R emit
2DUP + R> SWAP C! 1+ 0 THEN
ENDCASE
getcursor dispused PHORE
AGAIN ;
\ TASK CREATION
HEX
H: TASK \ values after INIT-TASKS:
CSEG CREATE HERE E92E , \ DISP 0 -- JMP ( task asleep )
DSEG CTASK @ , CTASK ! \ 02 -- relative addr nxt task
user @ , \ 04 -- size of user area (not used?)
0 , \ 06 -- SS register contents
user @ pssize 10 * + , \ 08 -- SP register contents
user @ pssize 10 * + rssize + , \ 0A -- BP register contents
, \ 0C -- PC contents
\ the following fields are for per-task variables
\ and could be selectively elimiated if not needed if space is
\ at a premium. In that case, offsets may need to be adjusted
\ for words which use latter fields.
0 , \ 0E -- Message list
0 , \ 10 -- Timer
0 , \ 12 -- Y cursor coordinate
0 , \ 14 -- X cursor coordinate
0 , \ 16 -- Exception frame pointer
DSEG HERE 80 ALLOT 22 + , \ 18 -- PAD, a per-task work area
;
0 [IF]
Initially, DISP 2 has absolute address of next task.
This value as well as DISP 6 get
filled in by INIT-TASKS when application is run.
[THEN]
CSEG CREATE MAIN-TASK \ Give it a name
HERE DSEG CTASK ! \ Task list points to it
80CD , \ DISP 0 -- INT 80 (task awake)
0 , \ 02 -- relative addr next task
0 , \ 04 -- NOT USED
0 , \ 06 -- SS register contents
0 , \ 08 -- SP register contents
0 , \ 0A -- BP register contents
0 , \ 0C -- PC contents
0 , \ 0E -- Message list
0 , \ 10 -- Timer
0 , \ 12 -- Y cursor coordinate
0 , \ 14 -- X cursor coordinate
0 , \ 16 -- Exception Frame Pointer
DSEG HERE 80 ALLOT 22 + , \ 18 -- PAD, a per-task work area
0 [IF]
DISP-2, 6, 12, and 14 get filled in by INIT-TASK. -8 -0A and -0C
are filled by first task swap (which is done by INIT-TASK).
[THEN]
\ TASK INITIALIZATION
0 0 IN/OUT
: INIT-TASKS \ This MUST be executed to start multitasking
CTASK @
BEGIN ?DUP WHILE \ for each task DO:
CELL+ DUP CS: @ IF \ one follows, this isn't main task
DUP 8 + CS: @ 10 + 4 RSHIFT GET
OVER 4 + CS: ! \ stackseg
DUP CS: @ TUCK \ next task
ELSE
0 SWAP CTASK @ \ next task is head of list
THEN
OVER - CELL- SWAP CS: !
REPEAT
MAIN-TASK CTASK !
getcursor \ sets main task cursor
?SS: MAIN-TASK 6 + CS: ! \ sets main task stack segment
start-timer
MULTI ( GO!!! ) ;
\ TASK DISPATCHER
CODE PAUSE
0 # ?multi [] CMP
=0 IF, RET THEN,
CTASK [] BX MOV \ current task
CS: 0C +[BX] POP \ save PC
BP CS: 0A +[BX] MOV \ save BP
SP CS: 08 +[BX] MOV \ save SP
CS: 2 +[BX] BX ADD
4 # BX ADD
CLI \ no ints during dispatch
BX JMPI ( dispatch )
END-CODE \ PAUSE
0 [IF]
Tasks are linked together so that jumping to a task will cause
jumping to the next if it is asleep, or doing an INT 80 if it
is awake. Thanks to Henry Laxen's Forth 83 model for the
technique.
[THEN]
L: start-task ( the INT80 routine )
BX POP
BX DEC
BX DEC \ Pointer to the task
CS: 6 +[BX] SS >SEG \ restore stack segment
CS: 8 +[BX] SP MOV \ restore SP
STI \ Interrupts are safe now
CS: 0A +[BX] BP MOV \ restore BP
BX CTASK [] MOV \ current task
CS: 0C +[BX] JMPI \ go!
FORTH \ start-task
0 [IF]
This code starts up a new task by setting up all registers,
fixing CTASK, and jumping to where we left off.
[THEN]
\ TASK MANAGEMENT
: SINGLE ?multi OFF ;
: MULTI ?multi ON
?CS: start-task 0 200 2!L \ install interrupt vector
PAUSE \ start with a task swap
;
1 0 IN/OUT
: WAKE 80CD CS: <- ;
1 0 IN/OUT
\ the 2e prefix byte (CS override) makes the jmp instruction 4 bytes long
: SLEEP ( task -- ) E92E CS: <- ;
1 1 IN/OUT
: WAITING? 10 + CS: @ 0<> ;
0 0 IN/OUT
: STOP CTASK @ SLEEP PAUSE ;
0 1 IN/OUT
: ACTIVE-TASKS
0 CTASK @
BEGIN
DUP WAITING? IF SWAP 1+ SWAP ELSE
DUP CS: @ 80CD = IF SWAP 1+ SWAP THEN THEN \ check for active
DUP CELL+ CS: @ + 4 + \ address of next task
DUP CTASK @ = UNTIL \ Loop until back to start
DROP ( task address )
;
\ MESSAGE PASSING
0 1 IN/OUT
: MESSAGE? CTASK @ 0E + CS: @ ;
0 1 IN/OUT
: GET-MESSAGE
BEGIN MESSAGE? ?DUP 0= WHILE STOP REPEAT
DUP 0 @L CTASK @ 0E + CS: ! \ Unlink message
;
1 1 IN/OUT
: MESSAGES
0 SWAP 0E + CS: @ ?DUP IF
BEGIN SWAP 1+ SWAP 0 @L ?DUP 0= UNTIL
THEN ;
2 0 IN/OUT
: SEND-MESSAGE
OVER 0 SWAP 0 !L \ set message's next field to NIL
DUP WAITING? 0= IF DUP WAKE THEN \ fire up receiving task
\ unless waiting for timer
0E + DUP CS: @ ?DUP IF \ Existing messages in queue
NIP
BEGIN DUP 0 @L ?DUP WHILE NIP REPEAT \ find end of list
0 !L \ store message at end of list
ELSE
CS: ! \ no existing messages, put at head of queue.
THEN
PAUSE ; \ Give it a chance to run
\ control-break handler
\ always gets control and (currently) dumps task information
2VARIABLE cb_save
1B CONSTANT cb_int
0 0 IN/OUT
: cbt
PAGE
SINGLE
end-timer
." Task statistics: "
MAIN-TASK \ start with first
BEGIN CR
HEX DUP 0 <# # # # # #> TYPE SPACE \ address
DUP WAITING? IF ." Waiting " DUP 10 + CS: @ . ." ticks" ELSE
DUP CS: @ 80CD = IF ." Active" ELSE ." Sleeping" THEN THEN
DUP CELL+ CS: @ + 4 + \ address of next task
DUP MAIN-TASK = UNTIL \ Loop until back to start
DROP ( task address )
bye
;
' cbt TASK cb-task
L: cb_handler ( actual interrupt handler )
80CD # CS: cb-task [] MOV \ wake cb task
STI
IRET FORTH
\ timer
1C CONSTANT t_int \ timer interupt vector number
CSEG
CREATE t_save 4 ALLOT \ original interupt vector
L: t_handler
PUSHF CS: t_save CALLF \ do original functions
BX PUSH
MAIN-TASK # BX MOV ( start of list )
BEGIN,
CS: 0 # 10 +[BX] CMP =0 ~ IF, ( non_zero time )
CS: 10 +[BX] DEC ( count down )
=0 IF, 80CD # CS: [BX] MOV THEN, ( wake task )
THEN,
CS: 2 +[BX] BX ADD
4 # BX ADD ( next task )
MAIN-TASK # BX CMP
=0 UNTIL, ( back at start? )
BX POP
IRET
FORTH \ t_handler
\ timer start and end 08:09 11/18/85
: start-timer \ and control-break handler
t_int get-handler t_save CS: 2!
?CS: t_handler t_int set-handler
cb_int get-handler cb_save 2!
?CS: cb_handler cb_int set-handler
;
: end-timer
t_save CS: 2@ t_int set-handler
cb_save 2@ cb_int set-handler
;
2 0 IN/OUT
: TIME-OUT ( ticks task -- ) DUP SLEEP 10 + CS: ! ;
1 0 IN/OUT
DECIMAL
: MS ( ticks -- ) 182 10000 */ CTASK @ TIME-OUT PAUSE ;
HEX
\ Exception Wordset
CODE CATCH
SI POP AX POP \ retAddr execAddr
CTASK [] BX MOV
BP DEC BP DEC SI [BP] MOV
BP DEC BP DEC SP [BP] MOV
BP DEC BP DEC CS: 16 +[BX] CX MOV CX [BP] MOV
BP CS: 16 +[BX] MOV
AX CALLI
[BP] AX MOV AX CS: 16 +[BX] MOV
AX AX XOR AX PUSH
4 +[BP] AX MOV 6 # BP ADD
AX JMPI
END-CODE
1 0 IN/OUT
CODE throw
CTASK [] BX MOV
CS: 16 +[BX] BP MOV [BP] BX MOV BX CS: 16 +[BX] MOV
2 +[BP] SP MOV AX PUSH
4 +[BP] AX MOV
6 # BP ADD AX JMPI
END-CODE
: THROW ?DUP IF CTASK @ 16 + CS: @ IF throw THEN
." Uncaught THROW: " . BYE THEN ;
DSEG 0A = [IF] DECIMAL [THEN]